#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <iostream>
using namespace std;

/* Serwer class*/

// Maksymalna wielkość wiadomości
#define MSG_SIZE 80

// Maksymalna liczba podłączonych klientów
#define MAX_CLIENTS 95 

// Standardowy port serwera
#define MYPORT 7400 

/** ExitClient
* This method will be used to clear client's data.
 ...
 ...
*/
void exitClient(int fd, /**< This is filedescriptorow. */
	fd_set *readfds, /**  This is set file descriptorow. */
	char fd_array[], /** This is array file descryptorow. */
	int *num_clients /** Numer of clients */
	) 
{
        int i;

        close(fd);
        FD_CLR(fd, readfds);
        for (i = 0; i < (*num_clients) - 1; i++)
                if (fd_array[i] == fd)
                        break;
        for (; i < (*num_clients) - 1; i++)
                (fd_array[i]) = (fd_array[i + 1]);
        (*num_clients)--;
}

/*! 
* /brief Main Function
* 
* Main function of server
*/ 
int main(int argc, char *argv[]) 
{
        int i = 0;
        int port;
        int num_clients = 0;
        int server_sockfd, client_sockfd;
        struct sockaddr_in server_address;
        int addresslen = sizeof(struct sockaddr_in);
        int fd;
        char fd_array[MAX_CLIENTS];
        fd_set readfds, testfds, clientfds;
        char msg[MSG_SIZE + 1];
        char kb_msg[MSG_SIZE + 10];
        int result;
	    string name; 

        // Uruchomienie serwera
        if (argc == 1 || argc == 3)
	{
         // Sprawdzanie parametrów programu. Pierwszy parametr numer portu, drugi parametr nazwa hosta. Domyślny port: 7400
		if (argc == 3) 
		{
                        if (!strcmp("-p", argv[1])) 
			{
                                sscanf(argv[2], "%i", &port);
                        } 
                        else 
			{
                                printf("Niewlasciwy parametr.\nOpcje: [-p PORT] HOSTNAME\n");
                                exit(0);
                        }
                } 
                else
                        port = MYPORT;

                printf("\n*** Start serwera (wpisz \"quit\" aby wyjsc): \n");

				// Utworzenie socketu dla serwera
                server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
                server_address.sin_family = AF_INET;
                server_address.sin_addr.s_addr = htonl(INADDR_ANY);
                server_address.sin_port = htons(port);
                bind(server_sockfd, (struct sockaddr *) &server_address, addresslen);

				// Nasłuchiwanie klientów
                listen(server_sockfd, 1);
                FD_ZERO(&readfds);
                FD_SET(server_sockfd, &readfds);
                FD_SET(0, &readfds);

                while (1) 
		{
                        testfds = readfds;
                        select(FD_SETSIZE, &testfds, NULL, NULL, NULL);

						// Sprawdzanie aktywności
                        for (fd = 0; fd < FD_SETSIZE; fd++) 
			{
                                if (FD_ISSET(fd, &testfds)) 
				{

                                        if (fd == server_sockfd) 
					{
                                                client_sockfd = accept(server_sockfd, NULL, NULL);
												
												// Dodawanie klienta do puli
                                                if (num_clients < MAX_CLIENTS) 
						{
                                                        FD_SET(client_sockfd, &readfds);
                                                        fd_array[num_clients] = client_sockfd;

                                                        printf("Klient %d polaczony\n", ++num_clients);

                                                        sprintf(msg, "c");

                                                        send(client_sockfd, msg, strlen(msg), 0);
                                                } 

                                                //Przekroczenie liczby klientów podłączonych
                                                else 
						{
                                                        sprintf(msg, "Za duzo klientow.\n");
                                                        write(client_sockfd, msg, strlen(msg));
                                                        close(client_sockfd);
                                                }
                                        } 
										// Keyboard activity
                                        else if (fd == 0)
					{
                                                fgets(kb_msg, MSG_SIZE + 1, stdin);

												//Kończenie pracy serwera - wysyłanie wiadomości do klientów 
                                                if (strcmp(kb_msg, "quit\n") == 0) 
						{
                                                        sprintf(msg, "Koniec serwera...\n");
                                                        for (i = 0; i < num_clients; i++) 
							{
                                                                write(fd_array[i], msg, strlen(msg));
                                                                close(fd_array[i]);
                                                        }
                                                        close(server_sockfd);
                                                        exit(0);
                                                } 

                                                // Wysyłanie wiadomości z serwera do klientów
                                                else 
						{
                                                        sprintf(msg, "c");
                                                        for (i = 0; i < num_clients; i++) 
							{
                                                                write(fd_array[i], msg, strlen(msg));
                                                        }
                                                }
                                        } 
                                        else if (fd) 
					{
                         // Czytanie danych z otwartego socketu
						result = read(fd, msg, MSG_SIZE); 
                                                if (result == -1)
                                                        perror("read()");
                                                else if (result > 0) 
						{
                                                        sprintf(kb_msg, "c");
                                                        msg[result] = '\0';

                                                        strcat(kb_msg, msg + 1); 

														// Przesyłanie wiadomości do innych klientów
                                                        for (i = 0; i < num_clients; i++) 
							{
                                                                if (fd_array[i] != fd)
                                                                        write(fd_array[i], kb_msg, strlen(kb_msg));
                                                        } 

														// Wypisywanie tekstu w serwerze
                                                        printf("Klient %d: %s", num_clients, kb_msg + 1);

														//Zamykanie klienta
                                                        if (msg[0] == 'X') 
							{
                                                                exitClient(fd, &readfds, fd_array,
                                                                                &num_clients);
                                                        }
                                                }
                                        } 
                                        else 
					{
                                                exitClient(fd, &readfds, fd_array, &num_clients);
                                        }
                                }
                        }
                }
        }

}

/*! \class Server distributedServer.cpp "<netinet/in.h>"  
 *  \brief This is a serwer class.
 *  \author Tadeusz Wieja
 * Implementacja serwera
 */